---
title: 从 `ChangeNotifier` 迁移
---

import old from "!!raw-loader!/docs/migration/from_change_notifier/old.dart";
import declaration from "/docs/migration/from_change_notifier/declaration";
import initialization from "/docs/migration/from_change_notifier/initialization";
import migrated from "/docs/migration/from_change_notifier/migrated";

import { Link } from "@site/src/components/Link";
import { AutoSnippet } from "@site/src/components/CodeSnippet";


<!---
Within Riverpod, `ChangeNotifierProvider` is meant to be used to offer a smooth transition from
pkg:provider.
-->
在 Riverpod 中，`ChangeNotifierProvider` 用于提供从 pkg:provider 的平滑过渡。

<!---
If you've just started a migration to pkg:riverpod, make sure you read the dedicated guide
(see <Link documentID="from_provider/quickstart" />).
This article is meant for folks that already transitioned to riverpod, but want to move away from
`ChangeNotifier` definetively.
-->
如果您刚刚开始迁移到 pkg:riverpod，请务必阅读专用指南
（请参阅<Link documentID="from_provider/quickstart" />）。
本文适用于已经过渡到 Riverpod，但想要彻底放弃 `ChangeNotifier` 的人们。

<!---
All in all, migrating from `ChangeNotifier` to `AsyncNotifer` requires a
paradigm shift, but it brings great simplification with the resulting migrated
code. See also <Link documentID="concepts/why_immutability" />.
-->
总而言之，从 `ChangeNotifier` 迁移到 `AsyncNotifer` 需要范式转换，
但它极大地简化了迁移后的代码。
另请参阅<Link documentID="concepts/why_immutability" />。

<!---
Take this (faulty) example:
-->
以这个（错误的）例子为例：
<AutoSnippet raw={old} />

<!---
This implementation shows several weak design choices such as:
- The usage of `isLoading` and `hasError` to handle different asynchronous cases
- The need to carefully handle requests with tedious `try`/`catch`/`finally` expressions
- The need to inkove `notifyListeners` at the right times to make this implementation work
- The presence of inconsistent or possibly undesirable states, e.g. initialization with an empty list
-->
该实现显示了几个薄弱的设计选择，例如：
- 使用 `isLoading` 和 `hasError` 处理不同的异步情况
- 需要仔细处理带有繁琐的 `try`/`catch`/`finally` 表达式的请求
- 需要在正确的时间调用 `notifyListeners` 才能使此实现发挥作用
- 存在不一致或可能不需要的状态，例如使用空列表进行初始化

<!---
Note how this example has been crafted to show how `ChangeNotifier` can lead to faulty design choices
for newbie developers; also, another takeaway is that mutable state might be way harder than it
initially promises.
-->
请注意这个示例是如何精心设计的，以向新手开发人员展示 `ChangeNotifier`
如何导致错误的设计方案；此外，另一个要点是可变状态可能比最初承诺的要困难得多。

<!---
`Notifier`/`AsyncNotifer`, in combination with immutable state, can lead to better design choices
and less errors.
-->
`Notifier`/`AsyncNotifer` 与不可变状态相结合，
可以带来更好的设计方案和更少的错误。

<!---
Let's see how to migrate the above snippet, one step at a time, towards the newest APIs.
-->
让我们看看如何将上述代码片段一步一步迁移到最新的 API。


<!---
## Start your migration
-->
## 开始迁移​
<!---
First, we should declare the new provider / notifier: this requires some thought process which
depends on your unique business logic.
-->
首先，我们应该声明新的提供者程序/通知者程序：这需要一些思维过程，这取决于您独特的业务逻辑。

<!---
Let's summarize the above requirements:
- State is represented with `List<Todo>`, which obtained via a network call, with no parameters
- State should *also* expose info about its `loading`, `error` and `data` state
- State can be mutated via some exposed methods, thus a function isn't enough
-->
我们总结一下上面的要求：
- 状态用 `List<Todo>` 表示，通过网络调用获得，不带参数
- 状态还应该公开有关其 `loading`、`error` 和 `data` 状态的信息
- 状态可以通过一些公开的方法进行改变，因此一个函数是不够的

:::tip
<!---
The above thought process boils down to answering the following questions:
1. Are some side effects required?
    - `y`: Use riverpod's class-based API
    - `n`: Use riverpod's function-based API
2. Does state need to be loaded asynchronously?
    - `y`: Let `build` return a `Future<T>`
    - `n`: Let `build` simply return `T`
3. Are some parameters required?
    - `y`: Let `build` (or your function) accept them
    - `n`: Let `build` (or your function) accept no extra parameters
-->
上述思考过程归结为回答以下问题：
1. 是否需要一些副作用？
    - `y`：使用 Riverpod 的基于类的 API
    - `n`：使用 Riverpod 的基于函数的 API
1. State 需要异步加载吗？
    - `y`：让 `build` 返回 `Future<T>`
    - `n`：让 `build` 简单地返回 `T`
1. 是否需要一些参数？
    - `y`：让 `build` （或你的函数）接受它们
    - `n`：让 `build` （或你的函数）不接受额外的参数
:::

:::info
<!---
If you're using codegen, the above thought process is enough.  
There's no need to think about the right class names and their *specific* APIs.  
`@riverpod` only asks you to write a class with its return type, and you're good to go.
-->
如果您使用的是 codegen，上述思考过程就足够了。  
无需考虑正确的类名及其*特定*的 API。  
`@riverpod` 仅要求您编写一个具有返回类型的类，然后就可以开始了。
:::

<!---
Technically, the best fit here is to define a `AutoDisposeAsyncNotifier<List<Todo>>`,
which meets all the above requirements. Let's write some pseudocode first.
-->
从技术上讲，这里最合适的是定义一个 `AutoDisposeAsyncNotifier<List<Todo>>`，
它满足上述所有要求。让我们先写一些伪代码。

<AutoSnippet language="dart" {...declaration}></AutoSnippet>

:::tip
<!---
Remember: use snippets in your IDE to get some guidance, or just to speed up your code writing.
See <Link documentID="introduction/getting_started" hash="going-further-installing-code-snippets" />.
-->
请记住：在 IDE 中使用代码片段可以获得一些指导，或者只是为了加快代码编写速度。
请参阅<Link documentID="introduction/getting_started" hash="going-further-installing-code-snippets" />。
:::

<!---
With respect with `ChangeNotifier`'s implementation, we don't need to declare `todos` anymore;
such variable is `state`, which is implicitly loaded with `build`.
-->
考虑到 `ChangeNotifier` 的实现，我们不再需要声明 `todos`；
这样的变量是 `state`，它是用 `build` 隐式加载的。

<!---
Indeed, riverpod's notifiers can expose *one* entity at a time.
-->
事实上，Riverpod 的通知者程序一次可以暴露*一个*实体。

:::tip
<!---
Riverpod's API is meant to be granular; nonetheless, when migrating, you can still define a custom
entity to hold multiple values. Consider using [Dart 3's records](https://dart.dev/language/records)
to smooth out the migration at first.
-->
Riverpod 的 API 是细粒度的；尽管如此，在迁移时，
您仍然可以定义自定义实体来保存多个值。首先考虑使用 [Dart 3 的记录](https://dart.dev/language/records)
来平滑迁移。
:::


<!---
### Initialization
-->
### 初始化​
<!---
Initalizing a notifier is easy: just write initialization logic inside `build`.  
We can now get rid of the old `_init` function.
-->
初始化通知者程序很简单：只需在 `build` 内编写初始化逻辑即可。  
我们现在可以摆脱旧的 `_init` 函数。

<AutoSnippet language="dart" {...initialization}></AutoSnippet>

<!---
With respect of the old `_init`, the new `build` isn't missing anything: there is no need to
initialize variables such as `isLoading` or `hasError` anymore.
-->
相对于旧的 `_init` ，新的 `build` 没有丢失任何内容：
不需要初始化 `isLoading` 或 `hasError`

<!---
Riverpod will automatically translate any asynchronous provider, via exposing an `AsyncValue<List<Todo>>`
and handles the intricacies of asynchronous state way better than what two simple boolean flags can do.
-->
Riverpod 将通过公开 `AsyncValue<List<Todo>>` 自动转换任何异步提供者程序，
并比两个简单的布尔标志更好地处理异步状态的复杂性。

<!---
Indeed, any `AsyncNotifier` effectively makes writing additional `try`/`catch`/`finally` an anti-pattern
for handling asynchronous state.
-->
事实上，任何 `AsyncNotifier` 都会有效地使编写额外的 `try`/`catch`/`finally` 成为处理异步状态的反模式。


<!---
### Mutations and Side Effects
-->
### 突变和副作用​
<!---
Just like initialization, when performing side effects there's no need to manipulate boolean flags
such as `hasError`, or to write additional `try`/`catch`/`finally` blocks.
-->
就像初始化一样，执行副作用时，无需操作布尔标志，
例如 `hasError`，或编写额外的 `try`/`catch`/`finally`

<!---
Below, we've cut down all the boilerplate and successfully fully migrated the above example:
-->
下面，我们删除了所有样板文件并成功完全迁移了上面的示例：
<AutoSnippet language="dart" {...migrated} />

:::tip
<!---
Syntax and design choices may vary, but in the end we just need to write our request and update
state afterwards. See <Link documentID="essentials/side_effects" />.
-->
语法和设计方案可能会有所不同，但最终我们只需要编写我们的请求并随后更新状态。
请参阅<Link documentID="essentials/side_effects" />。
:::

<!---
## Migration Process Summary
-->
## 迁移过程总结

<!---
Let's review the whole migration process applied above, from a operational point of view.
-->
让我们从操作的角度回顾一下上面应用的整个迁移过程。

<!---
1. We've moved the initialization, away from a custom method invoked in a constructor, to `build`
2. We've removed `todos`, `isLoading` and `hasError` properties: internal `state` will suffice
3. We've removed any `try`-`catch`-`finally` blocks: returning the future is enough
4. We've applied the same simplification on the side effects (`addTodo`)
5. We've applied the mutations, via simply reassign `state`
-->
1. 我们已将初始化从构造函数中调用的自定义方法移至 `build`
1. 我们删除了 `todos`、`isLoading` 和 `hasError` 属性：内部 `state` 就足够了
1. 我们已经删除了所有 `try`-`catch`-`finally` 块：返回 Future 就足够了
1. 我们对副作用应用了相同的简化（`addTodo`）
1. 我们已经通过简单地重新分配 `state` 应用了突变
